K-Means en R
------------
**Importar datos:**
.. code:: r
datos <- read.csv("EAM_2019-v2.csv", sep = ";", dec = ",", header = T)
print(head(datos))
.. parsed-literal::
ï..gasto_personal inversion_AF ventas
1 32334949 92544622 321070109
2 5669798 69317638 86597330
3 11081213 50853587 26727389
4 47892609 42426113 388005721
5 56410101 35164028 390070138
6 20536798 27028346 490639882
.. code:: r
dim(datos)
.. raw:: html
- 216
- 3
.. code:: r
str(datos)
.. parsed-literal::
'data.frame': 216 obs. of 3 variables:
$ ï..gasto_personal: int 32334949 5669798 11081213 47892609 56410101 20536798 14001910 38533922 21397321 3478034 ...
$ inversion_AF : int 92544622 69317638 50853587 42426113 35164028 27028346 25150313 23019658 22007282 21924323 ...
$ ventas : int 321070109 86597330 26727389 388005721 390070138 490639882 379365700 488079500 233432780 9932602 ...
.. code:: r
colnames(datos) <- c("Gasto_personal", "Inversión_AF", "Ventas")
Creamos una variable para almacenar solo las variables que queremos
analizar.
.. code:: r
df <- datos[, c("Gasto_personal", "Ventas")]
**Dispersión de los datos:**
.. code:: r
library(ggplot2)
.. code:: r
ggplot(data = df)+
geom_point(aes(x = Gasto_personal, y = Ventas))
.. image:: output_10_0.png
:width: 420px
:height: 420px
Escalamiento de variables:
~~~~~~~~~~~~~~~~~~~~~~~~~~
Para Normalizar usaremos la función ``scale()``.
.. math:: Normalización = X_{norm} = \frac{x_i-\overline{x}}{\sigma_x}
.. code:: r
df_scaled <- scale(df)
df_scaled <- data.frame(df_scaled) # Data Frame para ggplot2
print(head(df_scaled))
.. parsed-literal::
Gasto_personal Ventas
1 1.52915807 1.9822451
2 -0.47700945 -0.1241207
3 -0.06987862 -0.6619571
4 2.69964731 2.5835553
5 3.34046558 2.6021008
6 0.64151759 3.5055604
**Dispersión de los datos escalados:**
.. code:: r
ggplot(data = df_scaled)+
geom_point(aes(x = Gasto_personal, y = Ventas))
.. image:: output_15_0.png
:width: 420px
:height: 420px
**Distancia Euclideana:**
.. math:: D_1(x_i, x_j) =\sqrt{\sum_{k=1}^p{(x_{ix}-x_{jx})^2}}
Con la función ``dist()`` podemos calcular diferentes distancias con el
argumento ``method =``: ``"euclidean"``, ``"manhattan"``,
``"minkowski"``, entre otros. Cuando se usa la distancia ``"minkowski"``
se debe agregar el argumento ``p =``.
.. code:: r
dist_eucl <- dist(df_scaled, method = "euclidean")
**Matriz de distancias:**
De forma matricial se muestran las distancias entre todas las
observaciones. La diagonal es cero porque es la distancia entre cada
observación con ella misma.
Por ejemplo: la distancia Euclidiana entre la observación uno y dos es
2,9. Resta al cuadrado de la fila 1 con la fila 2 y el resultado se saca
raíz cuadrada así:
.. code:: r
print(sqrt((df_scaled[1,1]-df_scaled[2,1])^2+(df_scaled[1,2]-df_scaled[2,2])^2))
.. parsed-literal::
[1] 2.908863
Se sacará la matriz para las distancias entre las primeras 10
observaciones.
.. code:: r
print(round(as.matrix(dist_eucl)[1:10, 1:10], 1))
.. parsed-literal::
1 2 3 4 5 6 7 8 9 10
1 0.0 2.9 3.1 1.3 1.9 1.8 1.5 1.6 1.1 3.5
2 2.9 0.0 0.7 4.2 4.7 3.8 2.7 4.4 1.8 0.7
3 3.1 0.7 0.0 4.3 4.7 4.2 3.2 4.6 2.0 0.6
4 1.3 4.2 4.3 0.0 0.6 2.3 2.6 1.1 2.4 4.8
5 1.9 4.7 4.7 0.6 0.0 2.8 3.2 1.6 3.0 5.2
6 1.8 3.8 4.2 2.3 2.8 0.0 1.1 1.4 2.3 4.5
7 1.5 2.7 3.2 2.6 3.2 1.1 0.0 2.1 1.4 3.4
8 1.6 4.4 4.6 1.1 1.6 1.4 2.1 0.0 2.6 5.0
9 1.1 1.8 2.0 2.4 3.0 2.3 1.4 2.6 0.0 2.4
10 3.5 0.7 0.6 4.8 5.2 4.5 3.4 5.0 2.4 0.0
K-Means:
~~~~~~~~
La función más usada es ``kmeans()`` de la librería ``stats``.
Instalar el siguiente paquete: ``install.packages("stats")``
**sintaxis:** ``kmeans(x, centers, iter.max = 10, nstart = 1)``
``x``: Data Frame con los datos, agregaremos las distancias calculadas
con ``dist()``.
``centers``: son los :math:`k` clusters iniciales.
``iter.max``: número máximo de iteraciones. Por defecto es 10.
``nstart``: número de particiones iniciales aleatorias cuando
``centers`` es un número. Probar con ``nstart > 1``
.. code:: r
set.seed(1) # valor semilla para obtener siempre los mismos resultados.
.. code:: r
names(kmeans(dist_eucl, 10))
.. raw:: html
- 'cluster'
- 'centers'
- 'totss'
- 'withinss'
- 'tot.withinss'
- 'betweenss'
- 'size'
- 'iter'
- 'ifault'
``cluster``: un vector de enteros (desde 1:k) que indica el cluster al
que se asigna cada punto.
``centers``: una matriz con los centroides.
``withinss``: vector con los WCSS de cada cluster. El resultado es
después de definir la cantidad de clusters.
``size``: cantidad de puntos en cada cluster.
Número óptimo de clusters:
~~~~~~~~~~~~~~~~~~~~~~~~~~
**Método del codo:**
Se calculará el valor de WCSS aplicando K-Means aumentando la cantidad
de centroides.
.. code:: r
wcss = vector()
for (i in 1:10){
wcss[i] <- sum(kmeans(df_scaled, i)$withinss)
}
ggplot()+geom_line(aes(x = c(1:10), y = wcss), size = 1)+
geom_vline(xintercept = 6, color = "darkred")+
labs(title = "Método del codo",
x = "k clusters",
y = "WCSS")+
theme_light()
.. image:: output_34_0.png
:width: 420px
:height: 420px
Este método también lo podemos hacer con la función ``fviz_nbclust()``
del paquete ``factoextra``.
``install.packages("factoextra")``
.. code:: r
library(factoextra)
.. parsed-literal::
Warning message:
"package 'factoextra' was built under R version 4.1.3"
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
.. code:: r
fviz_nbclust(df_scaled, kmeans, method = "wss") +
geom_vline(xintercept = 6, linetype = 2)+
labs(subtitle = "Método del codo")
.. image:: output_37_0.png
:width: 420px
:height: 420px
Método de la silueta:
~~~~~~~~~~~~~~~~~~~~~
Este método también lo hacemos con la función ``fviz_nbclust()``
.. code:: r
fviz_nbclust(df_scaled, kmeans, method = "silhouette")+
labs(subtitle = "Método de la silueta")
.. image:: output_40_0.png
:width: 420px
:height: 420px
Método del gap estadístico:
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: r
fviz_nbclust(df_scaled, kmeans, method = "gap_stat")+
labs(subtitle = "Gap statistic method")
.. image:: output_42_0.png
:width: 420px
:height: 420px
Aplicación del K-Means con k óptimo:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:math:`k = 3`:
Recomiendo llamar el modelo de cualquier forma diferente a ``kmeans``
por este nombre se utiliza para algunas librerías como función y no como
una variable; por ejemplo, guardemos el resultado con el nombre de
``k_means``.
.. code:: r
k_means <- kmeans(dist_eucl, 3, iter.max = 300, nstart = 10)
**Cantidad de observaciones por cada cluster:**
¿Cómo cambia este resultado para 3 clusters?
.. code:: r
print(k_means$size)
.. parsed-literal::
[1] 15 46 155
Para visualizar los clusters en los datos originales usaremos la función
``clusplot()`` del paquete ``cluster``.
Instalar el paquete: ``install.packages("cluster")``
.. code:: r
library(cluster)
.. parsed-literal::
Warning message:
"package 'cluster' was built under R version 4.1.3"
.. code:: r
clusplot(df,
k_means$cluster, # el cluster que se le asigna a cada punto.
lines = 0, # para eliminar líneas en el gráfico.
labels = 4, # 4 para que ponga una etiqueta al número del cluster.
color = TRUE, # diferente colo para cada sector.
plotchar = FALSE, # con FALSE se quitan los símbolos diferentes para cada cluster.
span = TRUE, # TRUE para que represente el cluster con una elipse no como círculo.
main = "Clustering de empresas",
xlab = "Activos Totales",
ylab = "Ingresos Operacionales"
)
.. image:: output_51_0.png
:width: 420px
:height: 420px
Otra forma de visualizar los cluster con ``fviz_cluster()``
.. code:: r
fviz_cluster(k_means, data = df,
stand = FALSE, # FALSE para que no estandarice los datos, ya se había hecho esto.
show.clust.cent = TRUE, # TRUE para que muestre los centroides.
ellipse = TRUE, # Puede ser FALSE o TRUE
ggtheme = theme_minimal())
.. image:: output_53_0.png
:width: 420px
:height: 420px
Es posible calcular la media de cada variable por cluster utilizando los
datos originales:
.. code:: r
print(aggregate(df, by = list(cluster = k_means$cluster), mean))
.. parsed-literal::
cluster Gasto_personal Ventas
1 1 46055433 390956062
2 2 19831448 197627597
3 3 6394092 43446519
:math:`k = 6`:
.. code:: r
k_means <- kmeans(dist_eucl, 6, iter.max = 300, nstart = 10)
print(k_means$size)
.. parsed-literal::
[1] 2 115 37 12 20 30
.. code:: r
fviz_cluster(k_means, data = df,
stand = FALSE, # FALSE para que no estandarice los datos, ya se había hecho esto.
show.clust.cent = TRUE, # TRUE para que muestre los centroides.
ellipse = TRUE, # Puede ser FALSE o TRUE
ggtheme = theme_minimal())
.. image:: output_58_0.png
:width: 420px
:height: 420px
**Realizar el K-Means con distancias Manhattan y Minkowski con p igual
0.1, 0.5, 3 y 4.**
**Realizar lo anterior, pero sin estandarizar las variables**
**¿Para este caso en específico será necesario la estandarización de
variables?**
**Realizar el K-Means con las siguientes variables: Inversión en AF y
Ventas**